package com.ngmaster.socket;

import com.ngmaster.socket.connection.NetConnection;
import com.ngmaster.socket.connection.TcpConnection;
import com.ngmaster.socket.message.HeartbeatMessage;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 管理网络连接和账号信息
 * */
public class NetManager {

    private static Logger logger = LoggerFactory.getLogger(NetManager.class);

    private static NetManager instance = new NetManager();

    /** <网络链接ID,网络链接> */
    private Map<Long, NetConnection> netConnectionMap = new ConcurrentHashMap<>();

    private Map<ChannelHandlerContext, NetConnection> objectNetConnectionMap = new ConcurrentHashMap<>();

    /** <终端唯一ID,终端> */
    private Map<String, Terminal> terminalMap = new ConcurrentHashMap<>();
    /** <网络链接ID,终端> */
    private Map<Long, Terminal> connectionTerminalMap = new ConcurrentHashMap<>();

    private static AtomicLong connectionId = new AtomicLong(1);


    public static NetManager getInstance() {
        return instance;
    }

    public Map<Long, NetConnection> getNetConnectionMap() {
        return netConnectionMap;
    }

    public void setNetConnectionMap(Map<Long, NetConnection> netConnectionMap) {
        this.netConnectionMap = netConnectionMap;
    }

    public Map<String, Terminal> getTerminalMap() {
        return terminalMap;
    }

    public void setTerminalMap(Map<String, Terminal> terminalMap) {
        this.terminalMap = terminalMap;
    }

    public Map<ChannelHandlerContext, NetConnection> getObjectNetConnectionMap() {
        return objectNetConnectionMap;
    }

    public void setObjectNetConnectionMap(Map<ChannelHandlerContext, NetConnection> objectNetConnectionMap) {
        this.objectNetConnectionMap = objectNetConnectionMap;
    }

    public NetConnection getConnectionByCtx(ChannelHandlerContext ctx) {
        return this.objectNetConnectionMap.get(ctx);
    }

    public void addConnection(ChannelHandlerContext ctx) {
        NetConnection netConnection = new TcpConnection();
        netConnection.init(connectionId.getAndIncrement(), ctx, null);
        this.objectNetConnectionMap.put(ctx, netConnection);
        this.addNetConnection(netConnection);
    }

    public NetConnection getConnection(long connectionId) {
        return this.netConnectionMap.get(connectionId);
    }

    public void addNetConnection(NetConnection netConnection) {
        this.netConnectionMap.put(netConnection.getConnectionId(), netConnection);
    }

    public NetConnection removeNetConnection(long connnectionId) {
        this.removeTerminal(connnectionId);
        return this.netConnectionMap.remove(connnectionId);
    }

    public void addTerminal(Terminal terminal) {
        this.terminalMap.put(terminal.getIccid(), terminal);
        this.connectionTerminalMap.put(terminal.getNetConnection().getConnectionId(), terminal);
    }

    public Terminal removeTerminal(long connectionId) {
        Terminal terminal = this.connectionTerminalMap.remove(connectionId);
        if (terminal != null) {
            this.terminalMap.remove(terminal.getIccid());
        }
        return terminal;
    }

    /**
     * 接收并处理消息
     * */
    public void receive(ChannelHandlerContext ctx, ByteBuf data) {
        logger.info("receive message:" + ctx + ", data.length:" + data.readableBytes());
        NetConnection netConnection = getConnectionByCtx(ctx);
        if (netConnection == null) {
            logger.warn("receive message:" + ctx + ", connection is not found");
            return;
        }

        ProtocolMessage protocolMessage = MessageManager.getInstance().createMessage(data);
        if (protocolMessage != null) {
            String iccid = protocolMessage.getImei().toUpperCase();
            Terminal terminal = null;
            if (terminalMap.containsKey(iccid)) {
                terminal = this.terminalMap.get(iccid);
                terminal.setNetConnection(netConnection);
            } else {
                terminal = new Terminal(iccid, netConnection);
                this.addTerminal(terminal);
            }
            terminal.setActiveTime(System.currentTimeMillis());

            MessageManager.getInstance().handleMessage(protocolMessage);
        } else {
            logger.warn("receive message:" + ctx + ", message is not found");
        }

    }


    public void sendMessage(Terminal terminal, HeartbeatMessage protocolMessage) {
        if (terminal == null) {
            // TODO record log
            return;
        }
        TcpConnection tcpConnection = (TcpConnection) terminal.getNetConnection();
        ByteBuf out = tcpConnection.getChannelHandlerContext().alloc().buffer();
        // TODO write bytes
        out.writeBytes(protocolMessage.getByteArray().getBytes());
        tcpConnection.sendMessage(out);
        // TODO record log
    }

    public Terminal getTerminal(String iccid) {
        Terminal terminal = this.terminalMap.get(iccid.toUpperCase());
        if (terminal == null) {
            return TerminalNull.getInstance();
        }
        return terminal;
    }

    public static Logger getLogger() {
        return logger;
    }

    /**
     * 在线设备数量
     * */
    public int getOnlineDeviceCount() {
        return this.connectionTerminalMap.size();
    }

    /**
     * 增加链接过的设备数量
     * */
    public int getDeviceCount() {
        return this.terminalMap.size();
    }
}
